#include "cxfloat.h"
#include <sstream>
#include <iostream>
#include <stdlib.h>

using namespace std;


thread_local cxfloat *cxfloat_cache1;
thread_local cxfloat *cxfloat_cache2;
thread_local bool cxfloat_all_allocations_temporary=false;
thread_local std::vector<__mpfr_struct*>* cxfloat_temporaries=nullptr;


void cxfloat::set_temporary(mpfr_prec_t prec)
{
  set_temporary_real(prec);
  set_temporary_imag(prec);
}


void cxfloat::set_temporary_imag(mpfr_prec_t prec)
{
  if(cxfloat_temporaries->size() == 0)
  {
    allocate_mpfr_struct(imag_part);
    init_mpfr_value(imag_part, prec);
  }
  else
  {
    imag_part = cxfloat_temporaries->back();
    if(prec != mpfr_get_prec(imag_part))
    {
      free(imag_part->_mpfr_d);free( imag_part );
      allocate_mpfr_struct(imag_part);
      init_mpfr_value(imag_part, prec);
    }
    cxfloat_temporaries->pop_back();
  }
}


void cxfloat::set_temporary_real(mpfr_prec_t prec)
{
  if(cxfloat_temporaries->size() == 0)
  {
    allocate_mpfr_struct(real_part);
    init_mpfr_value(real_part, prec);
  }
  else
  {
    real_part = cxfloat_temporaries->back();
    if(prec != mpfr_get_prec(real_part))
    {
      free(real_part->_mpfr_d);free( real_part );
      allocate_mpfr_struct(real_part);
      init_mpfr_value(real_part, prec);
    }
    cxfloat_temporaries->pop_back();
  }
}


void init_cxfloat_cache(mpfr_prec_t p)
{
  bool oldtemp = cxfloat_all_allocations_temporary;
  cxfloat_all_allocations_temporary = false;
  cxfloat_cache1 = new cxfloat(0,0,p);
  cxfloat_cache2 = new cxfloat(0,0,p);
  cxfloat_all_allocations_temporary = oldtemp;
  
  cxfloat_temporaries = new vector<__mpfr_struct*>();
}


void set_cxfloat_precision(mpfr_prec_t p)
{
  cxfloat_cache1->set(0,0,p);
  cxfloat_cache2->set(0,0,p);
}


void clear_cxfloat_cache()
{
  if(cxfloat_cache1 != nullptr)
  {
    delete cxfloat_cache1;
  }
  if(cxfloat_cache2 != nullptr)
  {
    delete cxfloat_cache2;
  }
  
  if(cxfloat_temporaries != nullptr)
  {
    for(unsigned long int i=0;i<cxfloat_temporaries->size();++i)
    {
      free((*cxfloat_temporaries)[i]->_mpfr_d);
      free( (*cxfloat_temporaries)[i] );
    }
    cxfloat_temporaries->clear();
    delete cxfloat_temporaries;
  }
  
  cxfloat_cache1=nullptr;
  cxfloat_cache2=nullptr;
  
  cxfloat_temporaries=nullptr;
}


void init_cxfloat_cache(void *arg)
{
  mp_prec_t p = *((mp_prec_t*)arg);
  init_cxfloat_cache(p);
}


void set_cxfloat_precision(void *arg)
{
  mp_prec_t p = *((mp_prec_t*)arg);
  cxfloat_cache1->set(0,0,p);
  cxfloat_cache2->set(0,0,p);
}


void clear_cxfloat_cache(void *arg)
{
  clear_cxfloat_cache();
}


void cxfloat::print(string &re, string &im, int base) const
{
  stringstream ss;
  string s;
  
  char *tstr = new char[(int)(mpfr_get_prec(real_part))+2];
  mp_exp_t *expt = new mp_exp_t;
  mpfr_get_str(tstr, expt, base, 0, real_part, GMP_RNDN);
  if(tstr[0]=='-')
  {
    s=tstr;s.erase(0,1);
    ss<<"-"<<"0."<<s<<"@"<<*expt;
  }
  else
  {
    ss<<"0."<<tstr<<"@"<<*expt;
  }
  re=ss.str();
  
  delete[] tstr;
  delete expt;
  
  ss.clear();ss.str("");
  tstr = new char[(int)(mpfr_get_prec(imag_part))+2];
  expt = new mp_exp_t;
  mpfr_get_str(tstr, expt, base, 0, imag_part, GMP_RNDN);
  if(tstr[0]=='-')
  {
    s=tstr;s.erase(0,1);
    ss<<"-"<<"0."<<s<<"@"<<*expt;
  }
  else
  {
    ss<<"0."<<tstr<<"@"<<*expt;
  }
  im=ss.str();
  
  delete[] tstr;
  delete expt;
}


void cxfloat::print_real(string &re, int base, mpfr_prec_t prec, bool bold) const
{
  stringstream ss;
  ss.clear();ss.str("");
  string s;
  
  char *tstr = new char[(int)(mpfr_get_prec(real_part))+2];
  mp_exp_t *expt = new mp_exp_t;
  mpfr_get_str(tstr, expt, base, prec, real_part, GMP_RNDN);
  
  if(bold == true)
  {
    if(tstr[0]=='-')
    {
      s=tstr;s.erase(0,1);
      ss<<"-"<<s[0]<<".";
      s.erase(0,1);
      if(base!=10){ss<<s<<"\033[1m@"<<((*expt)-1)<<"\033[0m";}
      else{ss<<s<<"\033[1mE"<<((*expt)-1)<<"\033[0m";}
    }
    else
    {
      s=tstr;
      ss<<s[0]<<".";
      s.erase(0,1);
      if(base!=10){ss<<s<<"\033[1m@"<<((*expt)-1)<<"\033[0m";}
      else{ss<<s<<"\033[1mE"<<((*expt)-1)<<"\033[0m";}
    }
  }
  else
  {
    if(tstr[0]=='-')
    {
      s=tstr;s.erase(0,1);
      ss<<"-"<<s[0]<<".";
      s.erase(0,1);
      if(base!=10){ss<<s<<"@"<<((*expt)-1);}
      else{ss<<s<<"E"<<((*expt)-1);}
    }
    else
    {
      s=tstr;
      ss<<s[0]<<".";
      s.erase(0,1);
      if(base!=10){ss<<s<<"@"<<((*expt)-1);}
      else{ss<<s<<"E"<<((*expt)-1);}
    }
  }
  
  re=ss.str();
  
  delete[] tstr;
  delete expt;
}


void cxfloat::print_imag(string &re, int base, mpfr_prec_t prec, bool bold) const
{
  stringstream ss;
  ss.clear();ss.str("");
  string s;
  
  char *tstr = new char[(int)(mpfr_get_prec(imag_part))+2];
  mp_exp_t *expt = new mp_exp_t;
  mpfr_get_str(tstr, expt, base, prec, imag_part, GMP_RNDN);
  
  if(bold == true)
  {
    if(tstr[0]=='-')
    {
      s=tstr;s.erase(0,1);
      ss<<"-"<<s[0]<<".";
      s.erase(0,1);
      if(base!=10){ss<<s<<"\033[1m@"<<((*expt)-1)<<"\033[0m";}
      else{ss<<s<<"\033[1mE"<<((*expt)-1)<<"\033[0m";}
    }
    else
    {
      s=tstr;
      ss<<s[0]<<".";
      s.erase(0,1);
      if(base!=10){ss<<s<<"\033[1m@"<<((*expt)-1)<<"\033[0m";}
      else{ss<<s<<"\033[1mE"<<((*expt)-1)<<"\033[0m";}
    }
  }
  else
  {
    if(tstr[0]=='-')
    {
      s=tstr;s.erase(0,1);
      ss<<"-"<<s[0]<<".";
      s.erase(0,1);
      if(base!=10){ss<<s<<"@"<<((*expt)-1);}
      else{ss<<s<<"E"<<((*expt)-1);}
    }
    else
    {
      s=tstr;
      ss<<s[0]<<".";
      s.erase(0,1);
      if(base!=10){ss<<s<<"@"<<((*expt)-1);}
      else{ss<<s<<"E"<<((*expt)-1);}
    }
  }
  
  re=ss.str();
  
  delete[] tstr;
  delete expt;
}


void cxfloat::print_mantissa(string &re, string &im, int base) const
{
  stringstream ss;
  string s;
  
  char *tstr = new char[(int)(mpfr_get_prec(real_part))+2];
  mp_exp_t *expt = new mp_exp_t;
  mpfr_get_str(tstr, expt, base, 0, real_part, GMP_RNDN);
  if(tstr[0]=='-')
  {
    s=tstr;s.erase(0,1);
    ss<<s;
  }
  else
  {
    ss<<tstr;
  }
  re=ss.str();
  
  delete[] tstr;
  delete expt;
  
  ss.clear();ss.str("");
  tstr = new char[(int)(mpfr_get_prec(imag_part))+2];
  expt = new mp_exp_t;
  mpfr_get_str(tstr, expt, base, 0, imag_part, GMP_RNDN);
  if(tstr[0]=='-')
  {
    s=tstr;s.erase(0,1);
    ss<<s;
  }
  else
  {
    ss<<tstr;
  }
  im=ss.str();
  
  delete[] tstr;
  delete expt;
}


void compare_mantissas(const cxfloat &x1, const cxfloat &x2, unsigned long int &d_re, unsigned long int &d_im, int base)
{
  string re1, im1, re2, im2;
  x1.print_mantissa(re1, im1, base);
  x2.print_mantissa(re2, im2, base);
  
  long int ssize;
  
  if(re1.size()<re2.size()){ssize=re1.size();}
  else{ssize=re2.size();}
  d_re=0;
  for(long int i=0;i<ssize;i++)
  {
    if(re1[i]==re2[i]){d_re++;}
    else{break;}
  }
  
  if(im1.size()<im2.size()){ssize=im1.size();}
  else{ssize=im2.size();}
  d_im=0;
  for(long int i=0;i<ssize;i++)
  {
    if(im1[i]==im2[i]){d_im++;}
    else{break;}
  }
}


int compare_cxfloat_real(const void *a, const void *b)
{
  return mpfr_cmp((*(cxfloat*)a).real_part, (*(cxfloat*)b).real_part);
}


void sort_by_real(cxfloat *arr, long int size)
{
  qsort(arr, size, sizeof(cxfloat), compare_cxfloat_real);
}




















